using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace SpaceInvaders
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        public static int screenWidth, screenHeight;
        private int enemyWidth, enemyHeight;
        private Player player;
        List<List<Enemy>> enemies = new List<List<Enemy>>();
        private int enemyWaitToFireTime;
        private int totalEnemiesShooting;
        private Random rand;
        private double elapsedTime;
        public bool playerHit = false;
        private SpriteFont font;
        private int lives;
        private int score;
        private static int currentMovement = 0;
        private Texture2D explosion;
        private Point frameSize, frames, current;


        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            IsMouseVisible = true;
            rand = new Random();
        }



        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            screenWidth = this.GraphicsDevice.Viewport.Width;
            screenHeight = this.GraphicsDevice.Viewport.Height;
            totalEnemiesShooting = 1;
            enemyWaitToFireTime = 1000;
            enemyWidth = 25;
            enemyHeight = 25;
            lives = 3;
            score = 0;
            frameSize = new Point(120,120);
            frames = new Point(3,4);
            current = new Point(0,0);
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            player = new Player(this, 40, 25, "ship");
            Services.AddService(typeof(SpriteBatch), spriteBatch);
            Components.Add(player);
            explosion = Content.Load<Texture2D>("BlueExplosion");
            font = Content.Load<SpriteFont>("font");

            int rows = 11;
            int cols = 5;
            for (int row = 0; row < rows; row++)
            {
                List<Enemy> enemyRow = new List<Enemy>();

                for (int col = 0; col < cols; col++)
                {
                    Enemy enemy = new Enemy(this, enemyWidth, enemyHeight, GetInvader(col).one, GetInvader(col).two);
                    enemy.color = GetColor(col);
                    enemy.position = new Vector2(row * 40 + 200, col * 40 + 50);
                    enemyRow.Add(enemy);
                    Components.Add(enemy);

                }
                enemies.Add(enemyRow);
            }

            // TODO: use this.Content to load your game content here
        }

        public DualString GetInvader(int val)
        {
            switch (val)
            {
                case 0:
                    return new DualString("invader1a", "invader1b");
                case 1:
                    return new DualString("invader1a", "invader1b");
                case 2:
                    return new DualString("invader2a", "invader2b");
                case 3:
                    return new DualString("invader2a", "invader2b");
                case 4:
                    return new DualString("invader3a", "invader3b");
                case 5:
                    return new DualString("invader3a", "invader3b");

                default:
                    throw new Exception("case not found for image");
            }
        }

        public Color GetColor(int val)
        {
            switch (val)
            {
                case 0:
                    return Color.Blue;
                case 1:
                    return Color.Red;
                case 2:
                    return Color.Yellow;
                case 3:
                    return Color.Pink;
                case 4:
                    return Color.Purple;
                default:
                    return Color.Orange;
            }
        }

        public List<Enemy> GetFirstInColumn()
        {
            return enemies.Select(enemyRow => enemyRow[enemyRow.Count - 1]).ToList();
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        public void EnemyFire(int total)
        {
            List<Enemy> enemiesThatCanFire = GetFirstInColumn();


            //while(enemiesThatCanFire.Count > 0 && total > 0 )
            //{
            int enemytoFire = rand.Next(enemiesThatCanFire.Count);
            Enemy enemy = enemiesThatCanFire[enemytoFire];
            enemy.Fire();
            // enemiesThatCanFire.Remove(enemy);
            // }

        }
        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            elapsedTime += gameTime.ElapsedGameTime.Milliseconds;

            if (elapsedTime > enemyWaitToFireTime && !playerHit)
            {
                elapsedTime = 0;
                enemyWaitToFireTime = rand.Next(100, 3500);
                EnemyFire(4);
            }
            else if (playerHit && elapsedTime > 160)
            {
                elapsedTime = 0;
                current.X++;

                if (current.X == frames.X)
                {
                    current.X = 0;
                    current.Y++;

                    if (current.Y == frames.Y)
                    {
                        playerHit = false;
                        player = new Player(this, 40, 25, "ship");
                        Components.Add(player);
                        lives--;
                    }
                }
            }
            if (FlipEnemyDirection())
            {
                Enemy.direction *= -1;
                MoveEnemiesDown();
            }

            if (player.projectile != null)
                foreach (List<Enemy> enemyRow in enemies)
                {
                    if (player.projectile != null)
                        for (int enemy = 0; enemy < enemyRow.Count(); enemy++)
                        {
                            if (player.projectile.Collides(enemyRow[enemy]))
                            {
                                score += 100;
                                Components.Remove(enemyRow[enemy]);
                                Components.Remove(player.projectile);
                                enemyRow.Remove(enemyRow[enemy]);
                                player.canShoot = true;
                                player.projectile = null;
                                break;
                            }
                        }
                }

            foreach (var enemyRow in enemies)
            {
                foreach (var enemy in enemyRow)
                {
                    if (enemy.projectile != null && enemy.projectile.Collides(player))
                    {
                        playerHit = true;
                        Enemy.pause = true;
                        Components.Remove(player);
                        break;
                    }
                }
            }



            base.Update(gameTime);
        }

        public bool FlipEnemyDirection()
        {
            return enemies.Any(enemyRow => enemyRow.Where(enemy => enemy != null).Any(enemy => enemy.OutOfBounds()));
        }

        public void MoveEnemiesDown()
        {
            foreach (Enemy enemy in enemies.SelectMany(enemyRow => enemyRow.Where(enemy => enemy != null)))
                enemy.MoveDown();
        }


        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);
            spriteBatch.Begin();

            String livesStr = "lives ";
            String gameOverStr = "Game Over";
            String scoreStr = "Score: ";
            spriteBatch.DrawString(font, livesStr, Vector2.Zero, Color.White);
            spriteBatch.DrawString(font, scoreStr + " " + score, new Vector2(screenWidth / 2 - font.MeasureString(scoreStr).X / 2, 0), Color.Yellow);

            for (int i = 0; i < lives; i++)
            {
                spriteBatch.Draw(player.texture, new Rectangle(0 + (int)font.MeasureString(livesStr).X + 20 + (i * player.width + 20 * i), 5, player.width, player.height), Color.White);
            }
            if (playerHit)
            {
                spriteBatch.DrawString(font, gameOverStr, new Vector2(screenWidth - (int)font.MeasureString(gameOverStr).X, 0), Color.Red);
                spriteBatch.Draw(explosion,new Rectangle((int)player.position.X, (int)player.position.Y, player.width + 15,player.height+ 15), new Rectangle(current.X * frameSize.X, current.Y * frameSize.Y, frameSize.X, frameSize.Y) , Color.White);
            }


            spriteBatch.End();

            base.Draw(gameTime);
        }
    }

    public class DualString
    {
        public DualString(string one, string two)
        {
            this.one = one;
            this.two = two;
        }
       public string one { get; set; }
        public string two { get; set; }

    }
}
